
package zabbix

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"
)

type JsonRPCResponse struct {
	Jsonrpc string      `json:"jsonrpc"`
	Error   ZabbixError `json:"error"`
	Result  interface{} `json:"result"`
	Id      int         `json:"id"`
}

type JsonRPCRequest struct {
	Jsonrpc string      `json:"jsonrpc"`
	Method  string      `json:"method"`
	Params  interface{} `json:"params"`
	Auth    string      `json:"auth,omitempty"`
	Id      int         `json:"id"`
}

type ZabbixError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    string `json:"data"`
}

func (z *ZabbixError) Error() string {
	return z.Data
}

type API struct {
	url    string
	user   string
	passwd string
	id     int
	auth   string
}


const (
	//zabbix 服务器的IP和url
	IP = "0.0.0.0"
	Url = "http://"+IP+"/zabbix"
)

//server, user, passwd string,
func NewAPI(params ...string) (*API, error) {
	if len(params) == 3 && params[0] != "" && params[1] != "" && params[2] != "" {
		return &API{params[0] + "/api_jsonrpc.php", params[1], params[2], 1, ""}, nil
	}
	if len(params) == 1 && params[0] != "" {
		return &API{id: 1, url: Url + "/api_jsonrpc.php", auth: params[0]}, nil
	}
	return nil, errors.New("new API 参数错误")
}
func (api *API) GetAuth() string {
	return api.auth
}
func (api *API) ZabbixRequest(method string,data interface{}) (JsonRPCResponse, error) {
	// Setup our JSONRPC Request data x
	id := api.id
	api.id = api.id + 1
	jsonobj := JsonRPCRequest{"2.0", method, data, api.auth, id}
	encoded, err := json.Marshal(jsonobj)
	if err != nil {
		return JsonRPCResponse{}, err
	}
	// Setup our HTTP request
	//client := &http.Client{}
	tr := &http.Transport{ //解决x509: certificate signed by unknown authority
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{
		Timeout:   30 * time.Second,
		Transport: tr, //解决x509: certificate signed by unknown authority
	}
	request, err := http.NewRequest("POST", api.url, bytes.NewBuffer(encoded))
	if err != nil {
		return JsonRPCResponse{}, err
	}
	request.Header.Add("Content-Type", "application/json-rpc")
	if api.auth != "" {
		// XXX Not required in practice, check spec
		//request.SetBasicAuth(api.user, api.passwd)
		//request.Header.Add("Authorization", api.auth)
	}
	// Execute the request
	response, err := client.Do(request)
	if err != nil {
		return JsonRPCResponse{}, err
	}
	/**
	  We can't rely on response.ContentLength because it will
	  be set at -1 for large responses that are chunked. So
	  we treat each API response as streamed data.
	*/
	var result JsonRPCResponse
	var buf bytes.Buffer

	_, err = io.Copy(&buf, response.Body)
	if err != nil {
		return JsonRPCResponse{}, err
	}

	err = json.Unmarshal(buf.Bytes(), &result)
	if err != nil {
		return JsonRPCResponse{}, err
	}

	response.Body.Close()

	return result, nil
}

func (api *API) Login() (bool, error) {
	params := make(map[string]string, 0)
	params["user"] = api.user
	params["password"] = api.passwd

	response, err := api.ZabbixRequest("user.login",params)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		return false, err
	}

	if response.Error.Code != 0 {
		return false, &response.Error
	}

	api.auth = response.Result.(string)
	return true, nil
}

func (api *API) LogOut() {
	response, err := api.ZabbixRequest("user.logout",map[string]interface{}{})
	fmt.Println(response,err)
	fmt.Println("退出zabbix登录。。。。。。")
	if err != nil || response.Result != true {
		fmt.Printf("logout Error: %s\n", err)
	}

}

func LoginAndHandle(url string,user string,password string) (*API) {
	//定义url，zabbix登陆使用的用户名及密码

	api,err := NewAPI(url,user,password)
	if err != nil {
		fmt.Println("NewAPI  fail")
	}
	login,err :=api.Login()
	if login {
		fmt.Println("login auth ok")
	}else{
		fmt.Println("login auth fail",err)
	}
	//defer api.LogOut()
	return api
}


// 请求数据的封装，然后转给 AllMethodHandles() 函数使用
func AllMethodHandleData(methodData string) (map[string]interface{}) {
	var data map[string]interface{}
	jsonstr := fmt.Sprintf(`%s`,methodData)
	json.Unmarshal([]byte(jsonstr),&data)
	return data
}

// 将请求的方法，还有请求的数据，发送到zabbix，
func (api *API)AllMethodHandles(method string,methodData string) (interface{}) {
	data := AllMethodHandleData(methodData)
	result,err:=api.ZabbixRequest(method,data)
	if err != nil {
		return nil
	}
	if result.Error.Code != 0 {
		err = errors.New(result.Error.Message)
		return nil
	}
	resultdata := result.Result
	return resultdata
}

